package mapEditor.map;

import debug.*;

import mapEditor.MapEditor;
import mapEditor.obj.ImageObject;
import mapEditor.utils.Util;
import mapEditor.utils.ValueSpinner;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Color;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Ellipse2D;
import java.awt.image.ImageObserver;
import java.io.IOException;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;

import org.w3c.dom.Element;


public class MapGrid
{   
  public static final String BACKGROUND_IMAGE_IDENT="background";	
  
  private Image mapImage;
  private double mapWidth;  // In pixels
  private double mapHeight; // In pixels
  
  public Image getMapImage() { return(mapImage); }
  public double getMapWidth() { return(mapWidth*currentZoom); }
  public double getMapHeight() { return(mapHeight*currentZoom); }
  
//  private double zoomFactor=1.2;
  private double currentZoom=1;
//  public double getZoomFactor() { return(zoomFactor); }
  public double getCurrentZoom() { return(currentZoom); }
  public void setCurrentZoom(double currentZoom) { this.currentZoom=currentZoom; }
  
  private double mapGridOrigoEast=601150;   // X-Coordinate of Grid's Origo
  private double mapGridOrigoNorth=6645600; // Y-Coordinate of Grid's Origo
  private double gridOrigoX=48;  // Grid Origo's x in pixels
  private double gridOrigoY=163; // Grid Origo's y in pixels
  private double gridMeshWidthInPixels=325; // GridMesh size, x-direction, in pixels
  private double gridMeshHeightInPixels=325; // GridMesh size, y-direction, in pixels
  private double gridMeshWidthInMeter=50; // GridMesh size, x-direction, in meter
  private double gridMeshHeightInMeter=50; // GridMesh size, y-direction, in meter
  
  public double getGridOrigoEast() { return(mapGridOrigoEast); }
  public double getGridOrigoNorth() { return(mapGridOrigoNorth); }
  public double getGridOrigoX() { return(gridOrigoX*currentZoom); }
  public double getGridOrigoY() { return(gridOrigoY*currentZoom); }
  private double getGridMeshWidthInPixels() { return(gridMeshWidthInPixels*currentZoom); }
  private double getGridMeshHeightInPixels() { return(gridMeshHeightInPixels*currentZoom); }
  public double xPixelInMeter() { return(gridMeshWidthInMeter/getGridMeshWidthInPixels()); }  // E.G. 1 pixel == 50/325 meter 
  public double yPixelInMeter() { return(gridMeshHeightInMeter/getGridMeshHeightInPixels()); }  // E.G. 1 pixel == 50/325 meter 
  public double xMetersToPixel() { return(getGridMeshWidthInPixels()/gridMeshWidthInMeter); } // E.G. 1 meter == 325/50 pixels
  public double yMetersToPixel() { return(getGridMeshHeightInPixels()/gridMeshHeightInMeter); } // E.G. 1 meter == 325/50 pixels

  
  // *************************************************************************
  // *** CONSTRUCTOR: MapGrid
  // *************************************************************************
  public MapGrid()
  { if(mapImage!=null)
    { mapWidth=mapImage.getWidth(null);
	  mapHeight=mapImage.getHeight(null);
	  Debug.TRACE("NEW MapViewer: mapWidth="+mapWidth+", mapHeight="+mapHeight);
//	  requestResize();
    }
  }
  public MapGrid(Image background)
  { mapImage=background;
	if(mapImage!=null)
    { mapWidth=mapImage.getWidth(null);
	  mapHeight=mapImage.getHeight(null);
	  Debug.TRACE("NEW MapGrid: mapWidth="+mapWidth+", mapHeight="+mapHeight);
//	  requestResize();
    }
  }

  
//  public void zoom(boolean in)
//  { //Debug.TRACE("MapViewer.zoom: BEFORE: currentZoom="+currentZoom);
//	currentZoom=(in)?currentZoom*zoomFactor:currentZoom/zoomFactor;
//    //Debug.TRACE("MapViewer.zoom: AFTER: currentZoom="+currentZoom);
//  }
  
  public void writeGridMetaData(Element metaData)
  { printGridMetaData("writeGridMetaData");
	metaData.setAttribute("GridOrigoEast",""+mapGridOrigoEast);
    metaData.setAttribute("GridOrigoNorth",""+mapGridOrigoNorth);
    metaData.setAttribute("GridOrigoX",""+gridOrigoX);
    metaData.setAttribute("GridOrigoY",""+gridOrigoY);
    metaData.setAttribute("MeshWidthInPixels",""+gridMeshWidthInPixels);
    metaData.setAttribute("MeshHeightInPixels",""+gridMeshHeightInPixels);
    metaData.setAttribute("MeshWidthInMeter",""+gridMeshWidthInMeter);
    metaData.setAttribute("MeshHeightInMeter",""+gridMeshHeightInMeter);
  }
  
  public void readGridMetaData(Element metaData)
  { printGridMetaData("readGridMetaData("+metaData+")");
    if(metaData==null) return;
	String atr=metaData.getAttribute("GridOrigoEast"); if(atr.length()>0) mapGridOrigoEast=Double.parseDouble(atr);
    atr=metaData.getAttribute("GridOrigoNorth"); if(atr.length()>0) mapGridOrigoNorth=Double.parseDouble(atr);
    atr=metaData.getAttribute("GridOrigoX"); if(atr.length()>0) gridOrigoX=Double.parseDouble(atr);
    atr=metaData.getAttribute("GridOrigoY"); if(atr.length()>0) gridOrigoY=Double.parseDouble(atr);
    atr=metaData.getAttribute("MeshWidthInPixels"); if(atr.length()>0) gridMeshWidthInPixels=Double.parseDouble(atr);
    atr=metaData.getAttribute("MeshHeightInPixels"); if(atr.length()>0) gridMeshHeightInPixels=Double.parseDouble(atr);
    atr=metaData.getAttribute("MeshWidthInMeter"); if(atr.length()>0) gridMeshWidthInMeter=Double.parseDouble(atr);
    atr=metaData.getAttribute("MeshHeightInMeter"); if(atr.length()>0) gridMeshHeightInMeter=Double.parseDouble(atr);
    printGridMetaData("AFTER readGridMetaData");
  }
  
  public void printGridMetaData(String title)
  { Debug.println("*** "+title+" **********************************************************************************");
    Debug.println("*** GridOrigoEast:  "+mapGridOrigoEast);
    Debug.println("*** GridOrigoNorth: "+mapGridOrigoNorth);
    Debug.println("*** GridOrigoX: "+gridOrigoX);
    Debug.println("*** GridOrigoY: "+gridOrigoY);
    Debug.println("*** MeshWidthInPixels:  "+gridMeshWidthInPixels);
    Debug.println("*** MeshHeightInPixels: "+gridMeshHeightInPixels);
    Debug.println("*** MeshWidthInMeter:  "+gridMeshWidthInMeter);
    Debug.println("*** MeshHeightInMeter: "+gridMeshHeightInMeter);
    Debug.println("*** ********************************************************************************************");
  }
  
  public void setBackgroudImage(ImageObject backgroundImage)
  { //backgroundImage=image;
    if(backgroundImage!=null)
    try
    { URL url=backgroundImage.getURL();
      Image image=loadImage(url);	
      this.mapWidth=image.getWidth(null);
      this.mapHeight=image.getHeight(null);
      this.mapImage=image;
//	  requestResize();
    } catch(IOException e) { e.printStackTrace(); }
  }
  
//  // ******************************************************************
//  // *** UTILITY loadResourceImage
//  // ******************************************************************
//  private Image loadResourceImage(String name)
//  { URL url=this.getClass().getResource(name);
//    return(loadImage(url));
//  }  
  
  // ******************************************************************
  // *** UTILITY loadImage
  // ******************************************************************
  private Image loadImage(URL url)
  { Toolkit toolkit=Toolkit.getDefaultToolkit();
    Image image=toolkit.getImage(url);
    while(image.getWidth(null)<0) // Ensure loaded
    { try { Thread.sleep(100); } catch(InterruptedException ignore){} }
    return(image);
  }  

  // ******************************************************************
  // *** METHOD paintMap
  // ******************************************************************
  public void paintMap(Graphics g,ImageObserver imageObserver)
  {
	  g.drawImage(getMapImage(),0,0,(int)(getMapWidth()),(int)(getMapHeight()),imageObserver);
  }
  
  // ******************************************************************
  // *** METHOD paintGrid
  // ******************************************************************
  public void paintGrid(final Graphics2D g)
  { double width=mapWidth*currentZoom;
	double height=mapHeight*currentZoom;
	int n=(int)(mapWidth/gridMeshWidthInPixels)+2;
	g.setColor(Color.red);
	g.draw(new Ellipse2D.Double(getGridOrigoX()-10,getGridOrigoY()-10,20,20));
	for(int i= -1;i<n;i++)
	{ double y=getGridOrigoY()+i*getGridMeshHeightInPixels(); 
	  if(y<height) g.draw(new Line2D.Double(0,y,width,y));
	}
	n=(int)(mapHeight/gridMeshHeightInPixels)+2;
	g.setColor(Color.blue);
	for(int i= -1;i<n;i++)
	{ double x=getGridOrigoX()+i*getGridMeshWidthInPixels();
	  if(x<width) g.draw(new Line2D.Double(x,0,x,height));
	}
	g.setColor(Color.black);
  }
  
  //********************************************************************
  //*** INNER CLASS: GridCalibratingDialog
  //********************************************************************
  public class GridCalibratingDialog extends JDialog
  { static final long serialVersionUID=0; // AD'HOC
    static final double MINIMUM=0;
    static final double MAXIMUM=Integer.MAX_VALUE;

    private JButton okBtn;
    private JButton cancelBtn;

    // Initial Values:
    private double initialGridOrigoX;
    private double initialGridOrigoY;
    private double initialGridMeshWidthInPixels;
    private double initialGridMeshHeightInPixels;
    private double initialGridMeshWidthInMeter;
    private double initialGridMeshHeightInMeter;
    private double initialMapGridOrigoEast;
    private double initialMapGridOrigoNorth;
    private void saveInitialValues()
    { initialGridOrigoX=gridOrigoX;
      initialGridOrigoY=gridOrigoY;
      initialGridMeshWidthInPixels=gridMeshWidthInPixels;
      initialGridMeshHeightInPixels=gridMeshHeightInPixels;
      initialGridMeshWidthInMeter=gridMeshWidthInMeter;
      initialGridMeshHeightInMeter=gridMeshHeightInMeter;
      initialMapGridOrigoEast=mapGridOrigoEast;
      initialMapGridOrigoNorth=mapGridOrigoNorth;
    }
    private void restoreInitialValues()
    { gridOrigoX=initialGridOrigoX;    	  
 	  gridOrigoY=initialGridOrigoY;    	  
 	  gridMeshWidthInPixels=initialGridMeshWidthInPixels;    	  
 	  gridMeshHeightInPixels=initialGridMeshHeightInPixels;  
 	  gridMeshWidthInMeter=initialGridMeshWidthInMeter;    	  
 	  gridMeshHeightInMeter=initialGridMeshHeightInMeter;
 	  mapGridOrigoEast=initialMapGridOrigoEast;
 	  mapGridOrigoNorth=initialMapGridOrigoNorth;
 	  MapEditor.getCurrentMapCanvas().repaint();
    }

    // ******************************************************************
    // *** CONSTRUCTOR: GridCalibratingDialog
    // ******************************************************************
    public GridCalibratingDialog()
    { saveInitialValues();
      setBounds(300,200,370,350); // Popup to: x,y,width,height
      addWindowListener(new WindowAdapter()
      { public void windowClosing(WindowEvent we)
        { restoreInitialValues(); dispose(); }
      });
      setTitle("Coordinate System Calibration");
      JPanel center=new JPanel();
      center.add(getPixelPanel("Pixel Calibration"));
      center.add(new JLabel(" ")); // Space only
      center.add(getMeterPanel("Meter Calibration"));
      center.add(new JLabel(" ")); // Space only
      center.add(getLocationPanel("UTM32 Reference"));

      JPanel buttons=new JPanel();
      buttons.add(okBtn=new JButton("Ok"));
      okBtn.addActionListener(new ActionListener()
      { public void actionPerformed(ActionEvent e)
        { dispose(); }
      });
      buttons.add(cancelBtn=new JButton("Cancel"));
      cancelBtn.addActionListener(new ActionListener()
      { public void actionPerformed(ActionEvent e)
        { restoreInitialValues(); dispose(); }
      });
      setLayout(new BorderLayout());
      add(new JLabel(" "),BorderLayout.NORTH); // Space only
      add(center, BorderLayout.CENTER);
      add(buttons, BorderLayout.SOUTH);
      setAlwaysOnTop(true);
      setVisible(true);
    }
    
    // ******************************************************************
    // *** PRIVATE: getPixelPanel
    // ******************************************************************
    private JPanel getPixelPanel(String title)
    { JPanel panel=new JPanel();
      panel.setBorder(Util.getTitleBorder(title));
      panel.setLayout(new GridLayout(4,2));
      double stepSize=0.5;
      panel.add(new JLabel("Horizontal Grid Size:"));
      panel.add(new ValueSpinner(initialGridMeshWidthInPixels,MINIMUM,MAXIMUM,stepSize)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value) { gridMeshWidthInPixels=value; }
      });
      panel.add(new JLabel("Vertical Grid Size:"));
      panel.add(new ValueSpinner(initialGridMeshHeightInPixels,MINIMUM,MAXIMUM,stepSize)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value) { gridMeshHeightInPixels=value; }
      });
      panel.add(new JLabel("Grid Origo's X-Axis:"));
      panel.add(new ValueSpinner(initialGridOrigoY,MINIMUM,MAXIMUM,stepSize)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value) { gridOrigoY=value; }
      });
      panel.add(new JLabel("Grid Origo's Y-Axis:"));
      panel.add(new ValueSpinner(initialGridOrigoX,MINIMUM,MAXIMUM,stepSize)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value) { gridOrigoX=value; }
      });
      return(panel);
    }

    // ******************************************************************
    // *** PRIVATE: getMeterPanel
    // ******************************************************************
    private JPanel getMeterPanel(String title)
    { JPanel panel=new JPanel();
      panel.setBorder(Util.getTitleBorder(title));
      panel.setLayout(new GridLayout(2,2));
      double stepSize=0.5;
      panel.add(new JLabel("Horizontal Grid Meter:"));
      panel.add(new ValueSpinner(initialGridMeshWidthInMeter,MINIMUM,MAXIMUM,stepSize)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value) { gridMeshWidthInMeter=value; }
      });
      panel.add(new JLabel("Vertical Grid Meter:"));
      panel.add(new ValueSpinner(initialGridMeshHeightInMeter,MINIMUM,MAXIMUM,stepSize)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value) { gridMeshHeightInMeter=value; }
      });
      return(panel);
    }
 
    // ******************************************************************
    // *** PRIVATE: getLocationPanel
    // ******************************************************************
    private JPanel getLocationPanel(String title)
    { JPanel panel=new JPanel();
      panel.setBorder(Util.getTitleBorder(title));
      panel.setLayout(new GridLayout(2,2));
      double stepSize=1;
      panel.add(new JLabel("Grid Origo's UTM32 East(X):"));
      panel.add(new ValueSpinner(initialMapGridOrigoEast,MINIMUM,MAXIMUM,stepSize)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value) { mapGridOrigoEast=value; }
      });
      panel.add(new JLabel("Grid Origo's UTM32 North(Y):"));
      panel.add(new ValueSpinner(initialMapGridOrigoNorth,MINIMUM,MAXIMUM,stepSize)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value) { mapGridOrigoNorth=value; }
      });
      return(panel);
    }
  }  
  

}
